home *** CD-ROM | disk | FTP | other *** search
- #!/bin/sh
- # @(#)ckserial.sh 1.4 11/21/90
- #
- # ckserial - check zone file serial numbers
- #
- # ckserial checks the serial numbers of zone files to ensure
- # that they are sequenced properly. It keeps a checkpointed
- # set of data for comparison purposes.
- #
- # USAGE
- # ckserial [ bootfile ]
- #
- # BUGS
- # Assumes optional ttl field for SOA record is not used.
-
- BOOTFILE=/etc/named.boot
- CKPTDIR=ckpoint # relative to 'directory' in bootfile
- LOGFILE=/tmp/.ckslog$$
- SEDFILE=/tmp/.ckssed$$
- MAIL=/usr/ucb/Mail
- NOTIFY=hostmaster
-
- # change default bootfile if necessary
- if [ "$1" != "" ]; then
- if [ -f $1 ]; then
- BOOTFILE=$1
- else
- echo usage: `basename $0` "[ bootfile ]"
- exit 1
- fi
- fi
-
- trap "rm -f /tmp/.cks*$$; exit 1" 2 3
-
- # sed file for massaging zone data
- cat >$SEDFILE <<!
- /^;/d
- s/;.*//
- :join
- /([^)]*$/N
- s/\n[ ]*/ /
- t join
- s/(\(.*\))/\1/
- !
-
- # strip comments from zone data
- stripcomments() {
- sed -e '/^;/d' -e 's/;.*//' $*
- }
-
- # list zone files used by boot file
- # secondary zones are not checked. These are assumed to be correct
- # as far as transfers go because old serial numbers won't (shouldn't)
- # be transferred. Besides, I'm not sure of the semantics for determining
- # whether the last entry is a file or not. I suppose a simple test of
- # existence would work though.
- getzonefiles() {
- awk '\
- $1 == "cache" || $1 == "CACHE" || \
- $1 == "primary" || $1 == "PRIMARY" {
- printf "%s\n",$NF }' $1
- }
-
- # get zone file directory
- getzonedir() {
- stripcomments $1 | awk 'BEGIN { dir = "/"; }
- $1 == "directory" || $1 == "DIRECTORY" {
- dir = $2 }
- END {print dir}'
- }
-
- # get SOA serial number
- getserialnumber() {
- tr a-z A-Z <$1 | sed -f $SEDFILE | awk '$3 == "SOA" { print $6}'
- }
-
- # get mail address of person in charge of the zone
- getpersonincharge() {
- stripcomments $1 | tr a-z A-Z | awk '$3 == "SOA" {print $5}'
- }
-
- # get zone origin
- getzone() {
- # get zone from zone data file
- zone=`stripcomments $1 | tr a-z A-Z | awk '$3 == "SOA" {print $1}'`
- # if zone is current origin
- if [ "$zone" = "@" ]; then
- # get zone from bootfile
- zone=`grep $1 $BOOTFILE | awk '{print $2}'`
- fi
- echo $zone
- }
-
- # checkpoint a list of files
- checkpoint() {
- while [ "$1" != "" ]; do
- # copy file preserving modes/dates
- cp -p $1 $CKPTDIR/$1
- shift
- done
- }
-
- # get list of include files from zone file
- includefiles() {
- grep -i '^\$include' $1 | awk '{print $2}'
- }
-
- # is arg 1 < arg2
- # test integer and floating numbers
- lt() {
- awk 'BEGIN{ if ('$1' < '$2') exit 0; else exit 1; }'
- }
-
- # compare two files ignoring white space changes
- compare() {
- diff -b $1 $2 >/dev/null
- }
-
- # log a zonefile error message
- errlog() {
- ERRORFILE=$1 # save zone file for error reporting
- echo "$2" 1>&2
- echo "$2" >>$LOGFILE
- }
-
- # convert domain name to mail address
- domaintoaddr() {
- echo $1 | sed -e 's/\.$//' -e 's/\./@/'
- }
-
- # notify the person in charge of a zone of detected errors
- notifypersonincharge() {
- MB=`getpersonincharge $1`
- ZONE=`getzone $1`
- $MAIL -s "$ZONE zone configuration error" $NOTIFY `domaintoaddr $MB` <$LOGFILE
- echo " $NOTIFY, `domaintoaddr $MB` notified via mail."
- }
-
- processerrors() {
- # if errors found, notify person in charge
- if [ -f $LOGFILE ]; then
- notifypersonincharge $ERRORFILE
-
- # truncate error log file
- rm -f $LOGFILE
- fi
- }
-
- #########################
- # real work starts here #
- #########################
-
- # cd to the zone file data directory
- cd `getzonedir $BOOTFILE`
-
- ZONEFILES=`getzonefiles $BOOTFILE`
- SOAFILES=`grep -il '[ ]IN[ ]*SOA[ ]' $ZONEFILES | stripcomments`
- EXIT=0
-
- for f in $SOAFILES; do
- # process any logged errors
- processerrors
-
- # check for zone file existence
- if [ ! -f $f ]; then
- echo "$BOOTFILE: $f doesn't exist" 1>&2
- continue
- fi
-
- # if this zone file is new, checkpoint it
- if [ ! -f $CKPTDIR/$f ]; then
- checkpoint $f
- continue
- fi
-
- #get serial number of current version
- nserialno=`getserialnumber $f`
-
- #get serial number of checkpointed version
- oserialno=`getserialnumber $CKPTDIR/$f`
-
- #if serial number differs, continue
- if [ $nserialno != $oserialno ]; then
- # sanity check first
- if lt $nserialno $oserialno; then
- errlog $f "$f: serial number ($nserialno) < previous ($oserialno)"
- EXIT=1
- continue
- fi
-
- # checkpoint new zone file and included files
- checkpoint $f `includefiles $f`
- continue
- fi
-
- #if file differs from ckpoint - error
- if compare $f $CKPTDIR/$f; then
- : all ok
- else
- errlog $f "$f: zone file changed, but serial number didn't"
- EXIT=1
- continue
- fi
-
- # test included files as well
- for i in `includefiles $f`; do
- # if this zone file is new, checkpoint it
- if [ ! -f $CKPTDIR/$i ]; then
- checkpoint $i
- continue
- fi
-
- #if file differs from ckpoint - error
- if compare $i $CKPTDIR/$i; then
- : all ok
- else
- errlog $f "$f included changed zone data from $i, but serial number didn't change"
- EXIT=1
- continue
- fi
-
- # check for nested includes. these aren't handled
- # but we don't expect them either.
- if [ "`grep -i '\$include' $i`" != "" ]; then
- errlog $i "include file $i includes other files."
- EXIT=1
- fi
- done
- done
-
- # process any remaining errors
- processerrors
-
- rm -f /tmp/.cks*$$
-
- exit $EXIT
-